import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import {
  useGLTF,
  useAnimations,
  useKeyboardControls,
  PositionalAudio,
} from "@react-three/drei";
import {
  CapsuleCollider,
  RigidBody,
  euler,
  quat,
  vec3,
} from "@react-three/rapier";
import * as THREE from "three";
import { reducer, useConnect } from "../../utils/dva17";
import { NMold, NUser, RSetState } from "../../config/constants";
import { isMobile, socket, toFixed } from "../../utils";
import audios from "../../assets/audios";
import { PLAYER_STATUS } from "../../config/enum";

useGLTF.preload("./player/actor.gltf");

const SPEED = 4; // 移动速度
const JUMP = 7; // 跳跃速度
const frontVector = new THREE.Vector3(); // 前后移动
const sideVector = new THREE.Vector3(); // 左右移动
const velocity = new THREE.Vector3(); // 空间速度
const position = new THREE.Vector3(); // 玩家位置

let frame = 0; // 帧数
let outer;
// 玩家模型

const x = Math.floor(Math.random() * 5);
const z = Math.floor(Math.random() * 10);
export default ({ log = false }) => {
  /*----------------------------变量命名------------------------------*/
  const { direction, action } = useConnect(NMold);
  const { user } = useConnect(NUser);
  // 加载玩家
  // const { scene, animations }: any = useGLTF("./player/actor.gltf");
  const { scene, animations }: any = useGLTF(
    "./compressed/louvreGirlClothesA-pack.gltf"
  );
  const { ref, actions, names }: any = useAnimations(animations, scene);
  const player: any = useRef(); // 玩家的引用
  const [status, setStatus] = useState(PLAYER_STATUS.idle); // 玩家状态
  const [, get] = useKeyboardControls(); // 获取键盘事件

  const [onGround, setGround] = useState(false); // 是否在地面
  const footsteps: any = useRef(); // 脚步音频

  /*----------------------------生命周期-------------------------------*/
  // 在DOM结构更新后、渲染前执行
  useLayoutEffect(() => {
    scene.traverse(
      (obj) => obj.isMesh && (obj.receiveShadow = obj.castShadow = true)
    );
  }, []);
  // 运动状态
  useEffect(() => {
    switch (status) {
      case PLAYER_STATUS.run:
        actions[names[1]]?.fadeOut(0.2);
        actions[names[2]]?.reset().fadeIn(0.2).play();
        footsteps.current?.play();
        // 关闭镜头自动旋转
        clearTimeout(outer);
        outer = null;
        reducer(NMold, RSetState, { autoRotate: false });
        break;
      case PLAYER_STATUS.walk:
        actions[names[1]]?.fadeOut(0.2);
        actions[names[0]]?.reset().fadeIn(0.2).play();
        footsteps.current?.play();
        break;
      default:
        actions[names[2]]?.fadeOut(0.2);
        actions[names[1]]?.reset().fadeIn(0.2).play();
        footsteps.current?.stop();
        // 开启镜自动旋转
        if (outer) return;
        outer = setTimeout(() => {
          reducer(NMold, RSetState, { autoRotate: true });
        }, 15000);
        break;
    }
  }, [status]);

  /*----------------------------渲染帧-------------------------------*/
  useFrame((state: any, delta) => {
    if (!player.current) return;
    frame += 1;
    // 获取键盘输入
    const { forward, backward, left, right, jump }: any = get();

    // 玩家世界坐标
    position.copy(vec3(player.current.translation()));
    // 玩家移动前的相机距离，用来确保相机跟随玩家移动
    const distance = state.controls.getDistance();

    // 获取移动方向
    frontVector.set(0, 0, backward - forward); // 前后移动
    sideVector.set(left - right, 0, 0); // 左右移动

    if (!isMobile) {
      velocity
        .subVectors(frontVector, sideVector)
        .normalize()
        .multiplyScalar(SPEED)
        .applyEuler(state.camera.rotation); // 以相机方向为基准
    } else {
      velocity
        .copy(direction)
        .normalize()
        .multiplyScalar(SPEED)
        .applyEuler(state.camera.rotation);
    }

    // 移动
    // const newPos = velocity.clone().multiplyScalar(delta);
    // player.current.setTranslation(position.add(newPos), true);
    const target = {
      x: toFixed(velocity.x),
      y: player.current.linvel().y,
      z: toFixed(velocity.z),
    };
    position.y += 2;
    player.current.setLinvel(target);
    // socket同步,每10帧同步一次
    if (socket && frame % 10 === 0) {
      socket.emit("message", {
        id: user.id,
        velocity: target,
        position,
        jump: (jump || action) && onGround,
      });
    }
    // 跳跃
    if ((jump || action) && onGround) {
      jumpAction();
    }

    // 播放动画及人物旋转
    if (velocity.x !== 0 || velocity.z !== 0 || jump) {
      setStatus(PLAYER_STATUS.run);
      rotation();
    } else {
      // 人物静止
      setStatus(PLAYER_STATUS.idle);
    }

    // 更新相机位置
    updateCamera(state, distance, delta);

    // 打印
    logInfo();
  });

  /*----------------------------响应函数-------------------------------*/
  // 跳跃动作
  function jumpAction() {
    setGround(false);
    // player.current.applyImpulse({ x: 0, y: JUMP, z: 0 }, true);
    player.current.setLinvel({ x: 0, y: JUMP, z: 0 });
  }

  // 人物旋转;
  function rotation() {
    const rotationAngle = Math.atan2(velocity.x, velocity.z); // 旋转角度
    const rotationEuler = euler().set(0, rotationAngle, 0); // 旋转欧拉角
    const rotationQuaternion = quat().setFromEuler(rotationEuler); // 旋转四元数
    const startQuaternion = quat().copy(player.current.rotation()); // 当前旋转四元数

    startQuaternion.slerp(rotationQuaternion, 0.2);
    player.current.setRotation(startQuaternion);
  }

  // 更新相机位置
  function updateCamera(state, distance, delta) {
    const playerPos = player.current.translation(); // 玩家世界坐标

    const rotateDelta = (direction.x / 100) * delta;

    const { camera, controls } = state;
    controls.target.copy(playerPos);
    const spherical = new THREE.Spherical(
      distance,
      controls.getPolarAngle(),
      controls.getAzimuthalAngle() - rotateDelta
    );
    let position = new THREE.Vector3().setFromSpherical(spherical);
    camera.position.copy(playerPos).add(position);
  }
  // 打印信息
  function logInfo() {
    const { forward, backward, left, right, jump }: any = get();
    if (
      (forward || backward || left || right || jump) &&
      frame % 10 === 0 &&
      log
    ) {
      console.log(`--------------${frame}---------------`);
      console.log("onGround", onGround);
      console.log("direction", direction);
      console.log("position", position);
      console.log("rotation", player.current.rotation());
    }
  }

  /*----------------------------模型渲染-------------------------------*/
  return (
    <group dispose={null} position={[x, 10, z]}>
      <RigidBody
        name="玩家"
        ref={player}
        colliders={false}
        type="dynamic"
        enabledRotations={[false, false, false]}
        onCollisionEnter={({ manifold, other }) => {
          const result = manifold.solverContactPoint(0);
          if (!result) return;
          setGround(true);
          if (other.rigidBodyObject && log) {
            console.log("碰撞点： ", result);
            console.log("碰撞对象：", other.rigidBodyObject.name);
          }
        }}
      >
        <primitive ref={ref} object={scene} position={[0, -1, 0]} />
        <CapsuleCollider args={[0.6, 0.3]} position={[0, -0.1, 0]} />
        {/* 脚本音效 */}
        <PositionalAudio
          ref={footsteps}
          url={audios.footsteps}
          distance={2}
          loop
          load
        />
        {/* <Html center position={[0, 1, 0]}>
          <div className="name">贪天邪神</div>
        </Html> */}
      </RigidBody>
    </group>
  );
};
